MORSE CODE TUTOR

by Gordon Sweet

Home

API Translator

Window Help

Communication

Slider Control

Desktop Shortcuts

Quick Visual Designer

Rules for Code

DATA and Arrays

Easy Polygon

Triangle Draw

Control a Sprite

Shell to DOS

Morse Code

Batch Files

Multicolumn Listbox

Newsletter help

Index


Morse Code is now rarely used on the airwaves etc due to the rapid advance in many other much faster means of communication. In 2003 by International agreement, the majority of countries agreed it was no longer necessary for Amateur Radio operators to pass a speed test in Morse to qualify for a transmitting licence. Previously it was always deemed necessary in view of a few small ships etc still using the ancient from of communication, in case Radio Amateurs caused any interference. This especially applied when sharing the same bands, and the need to recognise and act on any distress signals received in Morse. However I know of at least two Radio Amateurs who still occasionally use Morse, pointing out the ease which long distance contacts can be made through bad atmospheric interference, that makes other forms on communication impossible. After all the speed at which most of us can type is probably no faster when using Internet chat rooms etc, than experienced Morse operators can send messages

All this explains the reason for the following Morse Tutor I upgraded to LB some time ago. I must admit one of the keen operators mentioned above is not too happy at the idea of an automated Morse creator, pointing out is has 'no Soul', showing no recognizable individuality of a human operator. But it could prove invaluable when learning Morse in the absence of an experienced operator. I believe during the wars if someone attempted to send morse pretending to be a captured spy, often his or her mode of operation was detected as being different.

Looking at the opening screen, the Morse Key button allows you to practice slow Morse using the mouse, subject to the limitations of mouse speed setup. The Lbug option is a rather crude attempt to emulate the automatic Morse Keys that generate a rapid repetition of Dot/Dashes. The Enter option generates Morse from text entered at the keyboard. Speed allows speeds of around 1 to 25 words per second. Set Tone controls the tone frequency, and access to a variety of instrument voices supplied with the Windows DLL. Not all of the available 127 instrument sounds are suitable, only mostly the wind instruments capable of producing a sustained note for the Morse Dash. You will notice among the [instr] Data the INTERNAL SPEAKER is also an option, which activates the little console speaker from the [sound] section, instead of instruments in the [newNote] section once initiated from the [sblast] section.

To enable the console speaker to be used, you must include the NTPORT.DLL supplied with LB, when distributing this program to others if using Windows 95/98. Read the instructions in README.TXT in the LB NTPORT directory regarding the need to also installthe ZNTPORT.SYS for others using Windows XP etc. The commonly used Morse Code Data is shown in end [morse] section. The Codes button displays a list or printout of these codes. TXT file will generate Morse from any TXT or ASCII file. Remember spaces produce no sound, and you will be prompted as to how many letters are to be read, until a halt occurs to give the only chance to stop reading the file.

I must acknowledge the great help provided by Alyce Watson in the past in creating the instrument tones, and the use of the Mouse as a simple Morse Key.

Gordon Sweet



   ' Morse Code Tutor/Generator
    nomainwin
    gosub [sblast]
    T = 16000 : note = 80 : SP = 16 : instrum = 81 : voice$ = "Square Wave"
    ux = 1 : uy = 1
    if DisplayWidth > 1000 then ux = 120 : uy = 90
[opts]
    UpperLeftX = ux : UpperLeftY = uy
    WindowWidth = 800 : WindowHeight = 580
    button #1, " MouseKey ", [mkey], LL 350, 80
    button #1, " Enter  ", [220], LL 110, 30
    button #1, " Speed  ", [420], LL 210, 30
    button #1, "Set Tone", [480], LL 310, 30
    button #1, " Codes  ", [540], LL 410, 30
    button #1, "TXT file", [640], LL 510, 30
    button #1, "  Quit  ", [quit], LL 610, 30
    menu #1, &Options,  &Morse, [220], &Speed, [420], &Tone, [540], &Codes, [540], &File, [640], |, &Quit, [860]
    REM M$(ASCI)=MORSE CHRS : TN=TONE  SP=SPEED
    DIM M$(126) : Dim voice(60): Dim ins$(60)
    FOR B = 0 TO 126 ' Dummy Data
        M$(B) = "*"
    NEXT B
    restore [morse]
    FOR S = 44 TO 57   ' Morse Codes
        READ r$ : M$(S)=r$ 
    NEXT S
    FOR S = 65 TO 90 ' Instrument Voices
        READ r$ : M$(S)= r$ 
    NEXT S
    REM CHRS =   space ' ; ?
    M$(32) = " ": M$(35) = " ": M$(39) = ".----.": M$(59) = "-.-.-.": M$(63) = "..--.."
    open "Morse Tests" for graphics_nsb as #1
    #1 "trapclose [quit]; cls; color black; place 250 100; fill 250 250 150"
    #1 "backcolor 250 250 150; font arial 24 bold"
    #1 "\Morse Test and Trainer"
    s$=N$(0) : #1 "color black; font arial 10 bold; place 250 130"
    #1 "\Ver. 3.4 by Gordon Sweet - August. 2003 - using Liberty Basic"
    #1 "\with help from the Hastings Electronics & Radio Club, U.K."
    #1 "font arial 12 bold; color blue; place 20 200"
    #1 "\     You can select various Voices using the Stereo Speakers, or tones from the internal Speaker."
    #1 "\The Speed can be changed, and output produced from the keyboard, .TXT file, or using the Mouse."
    #1 "\";space$(30);"Current Speed = ";SP;".  Voice = ";voice$;".  Tone = ";T/20
rem   (qbasic) H = (26 - SP) / 1.5: DOT = H / 4.5: DASH = H / 1.5
    H = (26 - SP) / 1.5: DOT = int(H *30/ 4.5): DASH = int(H *30/ 1.5)
    T$= "X" : C = LEN(T$)
    FOR R = 1 TO C
        G$ = MID$(T$, R, 1): F = ASC(G$)
        S$ = M$(F) : gosub [code]
    NEXT R
    win1 = 1 : wait

[220] ' Keyboard Entry
    close #1 : win1 = 0 : win2 = 1
    open "Morse Generator" for graphics_nsb as #2
    #2 "trapclose [quit2]"
    WHILE T$ <> "#" : Y=300
        T$="" : prompt "Enter Text."; T$ 
        if T$ = "" then [quit2]
        #2 "cls; font arial 24 bold; color darkgreen; place 200 40"
        #2 "\MORSE SOUND GENERATOR."
        #2 "font arial 14 bold; color red; place 250 80"
        #2 "\Invalid characters produce no sound"
        #2 "color black"
        IF T$ = "" THEN T$="#" : goto [400]
        T$ = upper$(T$)
        X=20 : Y=120
        #2 "place 20 300" : #2 "\";space$(200)
        C = LEN(T$)
        FOR R = 1 TO C
            G$ = MID$(T$, R, 1): F = ASC(G$)
            IF F < 32 OR F > 126 THEN GOTO [inval]
            g$="place "+str$(X)+" "+str$(Y) : #2 g$ 
            if CHR$(F) = "W" or CHR$(F) = "M" then X = X + 6
            if CHR$(F) = " " then X = X  - 6
            #2 "\"; CHR$(F)
            X=X+16
            if X>750 then Y=Y+25 : X=20
            S$ = M$(F) : gosub [code]
[inval]
        NEXT R
[400]
    wend
[quit2]
    close #2 : win2 = 0 : goto [opts]

[420] ' Select Speed
    close #1 : win1 = 0 : SP$ = str$(SP)
    prompt "Enter new Speed 1 to 25"; SP$ 
    SP=val(SP$)
    IF SP$ = "" or SP < 1 or SP > 25 THEN notice "INVALID !!" : SP = 16
    goto [opts]

[480] ' Select Tone
    close #1 : win1 = 0
    open "Tones" for graphics_nsb as #99
    print #99, "trapclose [cquit]; fill 100 100 1"
    WindowWidth=470 : WindowHeight=100
    UpperLeftX=200+ux : UpperLeftY=200+uy
    'read instrument names into array for combobox
    restore [instr]
    For vc = 1 to 57
        Read dat,data$
        voice(vc)=dat : ins$(vc)=data$ 
    Next vc
    vc = 1
    if instrum = 0 then gosub [sblast]
    instrum = 81 'first data
    msg$="    Select a Voice or Internal Speaker. If necessary turn up your speakers."

    Button #p.default, " Tone ",[tone],UL,270,15,50,30
    Button #p.default, "Accept",[cquit],UL,370,15,50,30
    Statictext #p, "Select Voice or Internal Speaker",15,5,200,20
    Combobox #p.ins, ins$(,[instrument],15, 25, 220,400
    Graphicbox #p.g, 0, 0,0,0
    Statictext #p.vol, msg$,15,60,550,40
    Open "Select" For Window_nf As #p
    Print #p.ins, "select Square Wave"
    Print #p, "trapclose [cquit]"
    Print #p.g, "trapclose [cquit]"
    mainH=hWnd(#p) : gosub [boxhold]
    Wait

[instrument]'user selected an instrument voice
    Print #p.ins, "selectionindex?"
    Input #p.ins, choice
    instrum = voice(choice)
    voice$ = ins$(choice)
[which]
    Wait

[tone]
    tone$ = str$(T/20)
    prompt "Enter Tone 100/3000"; tone$ : tone=val(tone$)
    if tone = 0 then [which]
    if tone < 100 or tone > 3000 then beep : goto [tone]
    T = tone * 20 : note = tone/10
    if note > 120 then note = 120
    goto [which]

[cquit] Close #p : close #99
    if instrum = 0 then gosub [vquit]
    goto [opts]

[540] ' Display Morse
    close #1 : win1 = 0
    WindowWidth=800 : WindowHeight=580
    UpperLeftX=ux : UpperLeftY=uy
    button #3, "Continue", [quit3], LL 300, 1
    button #3, "Printout", [pout], LL 400, 1
    open "Codes List" for graphics_nsb as #3
    #3 "trapclose [quit3]; cls; color blue; font arial 20 bold; place 230 20"
    #3 "\International Morse Code."
    #3 "font arial 14 bold; place 300 50"
    #3 "\Non code shown as *"
    #3 "color black"
    X=100 : Y=100
    FOR K = 39 TO 90
        if K=40 then K=44
        g$="place " + str$(X) + " " + str$(Y) : #3 g$
        #3  "\ASCI= "; K; " ";CHR$(K); "  "; M$(K)
        X=X+200
        if X>600 then Y=Y+25 : X=100
    NEXT K
[which1]
    win3 = 1 : wait
[quit3]
    close #3 : win3 = 0 : goto [opts]

[pout] ' Printout
    lprint space$(23);"MORSE CHARACTER CODES.":lprint
    for p=39 to 90
        lprint space$(25);"ASCI= "; p; " ";CHR$(p); "  "; M$(p)
        next p
        dump
    notice "Data passed to printer" : goto [which1]

[640] ' Read TXT files
    close #1 : win1 = 0 : win2 = 1
    WindowWidth=800 : WindowHeight=580
    UpperLeftX=ux : UpperLeftY=uy
    open "TXT Files" for graphics_nsb as #2
[pick]
    #2 "cls; font arial 20 bold; color red; place 30 350"
    #2 "\YOU CAN ONLY STOP THE MORSE AFTER A PAUSE."
    prompt "Enter number of letters until a PAUSE to Abort"; W$ : W=val(W$)
    if W=0 then playwave "dong.wav" : close #2 : goto [opts]
    filedialog "ONLY .TXT files","*.txt",F$
    if F$="" then playwave "dong.wav" : close #2 : goto [opts]
    #2 "flush; cls; font arial 14 bold; place 150 20"
    #2 "\NOTE spaces and invalid characters produce no sound!"
    #2 "color blue; place 150 40"
    #2 "|Reading File= ";F$
    #2 "color black"
    X=20 : Y=120 : K=0 : t$="\"
    open F$ for input as #scn
[read]
    if eof(#scn) <> 0 then [quit4]
    line input#scn, T$ : T$ = upper$(T$) : C = LEN(T$)
    FOR R = 1 TO C
        G$ = MID$(T$, R, 1) : F = ASC(G$)
        IF F < 32 OR F > 126 THEN GOTO [inval2]
        g$="place "+str$(X)+" "+str$(Y)
        #2 g$ : #2 "\"; CHR$(F)
        if CHR$(F) = "W" or CHR$(F) = "M" then X = X + 6
        if CHR$(F) = " " then X = X  - 6
        X=X+16
        if X>750 then Y=Y+25 : X=20
        S$ = M$(F): L = LEN(S$)
        FOR N = 1 TO L
            G$ = MID$(S$, N, 1)
            IF G$="-" THEN J = DASH : if instrum = 0  then gosub [sound] else gosub [newNote]
            IF G$="." THEN J = DOT : if instrum = 0  then gosub [sound] else gosub [newNote]
            IF G$=" " OR G$="*" THEN gosub [space]
            gosub [space] : REM = pause between dot or dash
        NEXT N
        K=K+1
        gosub [space] :  REM = extra pause between letters
        q$="yes" : if K=W then K=0 : confirm "OK to continue Y/N?"; q$ :
        if q$="no" then R=9999 : goto [quit4]
[inval2]
    NEXT R
    #2 "posxy x y"
    if y>750 then #2 "cls; place 20 20" : X=20 : Y=120
    goto [read]
[quit4]
    close #scn
    confirm "Select another TXT file Y/N?"; q$ : if q$="yes" then goto [pick]
    close #2 : win2 = 0 : goto [opts]

[code]
    L = LEN(S$)
    FOR N = 1 TO L
        G$ = MID$(S$, N, 1)
        IF G$=" " OR G$="*" THEN gosub [space] : goto [done]
        IF G$="-" THEN J = DASH
        IF G$="." THEN J = DOT
        if instrum = 0 then gosub [sound] : goto [done]
        gosub [newNote]
        gosub [space] : REM = pause between dot or dash
[done]
    NEXT N
    gosub [space] :  REM = extra pause between letters
    return

[space]
    S = time$("ms") : while time$("ms") < S + J : wend
    return

[mkey]
    close #1 : win1 = 0
    WindowWidth=800 : WindowHeight=580
    UpperLeftX=ux : UpperLeftY=uy
    button #4, " Lbug ", [lbug], UL 320, 380
    button #4, " Abort", [kquit], UL 420, 380
    open "Morsekey" for graphics_nsb as #4
    #4 "trapclose [kquit]; fill green; backcolor green"
    #4 "color blue; font arial 48 bold; place 240 100"
    #4 "\Morsekey"
    #4 "font arial 16 bold; color black; place 50 200"
    #4 "\Due to the Mouse system settings, it cannot be used as manual key"
    #4 "place 180 300"
    #4 "\Use Left click for a DOT,  Right click for a DASH"
    #4 "\LBUG repeats DOTs or DASHes, until reclicked."
    #4 "when leftButtonDown [dot]; when rightButtonDown [dash]"
    bug = 0 : #4 "color red"
[more]
    #4 "place 270 400"
    if bug = 0 then #4 "\OFF" else #4 "\ON   "
    win4 = 1 : wait
[lbug]
    if bug = 1 then bug = 0 else bug = 1
    goto [more]
[kquit]
    if instrum = 0 then gosub [clean]
    close #4 : win4 = 0 : goto [opts]
[dot] if bug = 1 then J = DOT : goto [bug]
    J = 100: goto [noise]
[dash]
    if bug = 1 then J = DASH : goto [bug]
    J = 200
[noise]
    if instrum = 0 then gosub [sound] : goto [ifbug]
    gosub [newNote]
[ifbug]
    if bug=0 then goto [more]
    goto [space] : return


[bug]
    flag$="none"
[check]
    CallDLL #user32, "GetAsyncKeyState", _
    _VK_LBUTTON AS long, leftdown AS long

    CallDLL #user32, "GetAsyncKeyState", _
    _VK_RBUTTON AS long, rightdown AS long

    newflag$="none"

    if leftdown then
        newflag$="left"
        gosub [noise] : gosub [space]
    end if

    if rightdown then
        newflag$="right"
        gosub [noise] : gosub [space]
    end if

    if newflag$<>flag$ then
        flag$=newflag$
    end if
    SCAN
    goto [check]

[sound]
    DJ=INT(J*2.5)
    Orig(0) = INP(hexdec("43"))
    Orig(1) = INP(hexdec("61"))
    OUT hexdec("43"), hexdec("B6")         ' link timer to freq divisor
    OUT hexdec("42"), 0            ' Set freq for silence
    OUT hexdec("42"), 0            ' No surprise sounds this way.
    OUT hexdec("61"), hexdec("4F")         ' Turn speaker ON

    CALL FREQ T
    S = time$("ms") : while time$("ms") < S + DJ : wend

[clean]
    OUT hexdec("43"), Orig(0)
    OUT hexdec("61"), Orig(1)
    return

    SUB FREQ F  ' Sets Frequency
    Div = INT(119000 / F)
    lDiv = Div and 255
    hDiv = int(Div / 256)
    OUT hexdec("42"), hDiv
    OUT hexdec("42"), lDiv
    END SUB

[sblast]
    Open "gdi32.dll" For DLL As #gdi
    'open midi device and obtain handle, midi functions return 0 if successful
    struct m, a$ As ptr
    CallDLL #winmm, "midiOutOpen",_
        m As struct,-1 As long,0 As long,0 As long,0 As long,ret As long
    hMidiOut=m.a$.struct    'handle to midi device

    vol=Hexdec("FFFF")    'sets to full volume
    CallDLL #winmm, "midiOutSetVolume",hMidiOut As ulong,vol As ulong, ret As ulong
    return

[vquit]'stop note, close midi device, DLLs, window
    event=128    'event = stop play
    low=(note*256)+event
    dwMsg=low+hi
    CallDLL #winmm, "midiOutShortMsg",hMidiOut As ulong,dwMsg As ulong, ret As ulong
    CallDLL #winmm, "midiOutClose", hMidiOut As ulong,ret As ulong
    CallDLL#user32,"ReleaseDC",Wnd As long,hDC As long,result As long
    Close #gdi
    return

[newNote]
    if instrum = 0 then return
'mouse clicked to start new note. First, stop old note from playing
    event=128    'event = stop play
    low=(note*256)+event
    dwMsg=low+hi
    CallDLL #winmm, "midiOutShortMsg",hMidiOut As ulong,dwMsg As ulong, ret As ulong
    gosub [startNote]   'sets new note value
    oldNote=note

    'signal a change. dwMsg=(velocity*256*256)+(voice*256)+event
    event=192  'event = change
    voice=instrum-1
    velocity=64
    low=(voice*256)+event
    hi=velocity*256*256
    dwMsg=low+hi
    CallDLL #winmm, "midiOutShortMsg",hMidiOut As ulong,dwMsg As ulong, ret As ulong
    'play new note. dwMsg=(velocity*256*256)+(note*256)+event
    event=144   'event = play
    velocity=64
    low=(note*256)+event
    hi=velocity*256*256
    dwMsg=low+hi
    CallDLL #winmm, "midiOutShortMsg",hMidiOut As ulong,dwMsg As ulong, ret As ulong
    S = time$("ms") : while time$("ms") < S + J : wend
    goto [endNote]

[startNote]
    CallDLL #gdi, "GetPixel",hDC As long,MX As long,MY As long,keyColor As long
    RETURN


[endNote]'stop note from playing
    event=128    'event = stop play
    low=(note*256)+event
    dwMsg=low+hi
    CallDLL #winmm, "midiOutShortMsg",hMidiOut As ulong,dwMsg As ulong, ret As ulong
    return

[quit]
    if win1 = 1 then close #1
    if win2 = 1 then close #2
    if win3 = 1 then close #3
    if win4 = 1 then close #4
    confirm "OK to Quit Y/N?"; q$
    if q$="no" then goto [opts]
    if instrum > 0 then gosub [vquit]
    end

[boxhold]
    open "user32" for dll as #user ' prevents losing box
        toTop=(-1 or 0)
        flags=_SWP_NOMOVE or _SWP_NOSIZE
    calldll #user,"SetWindowPos",mainH as ushort,toTop as short,_
    0 as short,0 as short,0 as short,0 as short,flags as ushort,_
    result as void
    close #user : return


[morse]
    REM MORSE Alphabet Chrs "," to 9  Asci 44/57
    DATA "--..--","-....-",".-.-.-","-..-.","-----",".----"
    DATA "..---","...---","....-",".....","-....","--...","---..","----."
    REM A to Z   Asci 65/90
    DATA ".-","-...","-.-.","-..",".","..-.","--.","....","..",".---","-.-",".-.."
    DATA "--","-.","---",".--.","--.-",".-.","...","-","..-","...-",".--","-..-","-.--","--.."
    end

[instr]  ' Instrumental Voices
Data 81, "Square Wave",83,"Sawtooth",0,"INTERNAL SPEAKER",17,"Hammond Organ"
Data 18,"Percussion Organ",19,"Rock Organ"
Data 29,"Church Organ",21,"Reed Organ",22,"Accordian",23,"Harmonica"
Data 24,"Tango Accordian",32,"Guitar Harmonic"
Data 41,"Violin",42,"Viola",43,"Cello"
Data 44,"Contrabass",45,"Tremolo Strings"
Data 46,"Pizzicato Strings",49,"String Ensemble One"
Data 50,"String Ensemble Two",51,"Synth Strings One",52,"Synth Strings Two"
Data 53,"Choir Ahhs",54,"Voice Oohs",55,"Synth Voice"
Data 57,"Trumpet",58,"Trombone",59,"Tuba",60,"Mute Trumpet",61,"French Horn"
Data 62,"Brass Section",63,"Synth Brass One",64,"Synth Brass Two"
Data 65,"Soprano Sax",66,"Alto Sax",67,"Tenor Sax",68,"Bari Sax",69,"Oboe"
Data 70,"English Horn",71,"Bassoon",72,"Clarinet",73,"Piccolo",74,"Flute"
Data 75,"Recorder",76,"Pan Flute",79,"Whistle",125,"Phone Ring"
Data 80,"Ocarina",83,"Caliope",84,"Chiff Lead"
Data 85,"Charang",86,"Solo Synth VX",87,"Brite Saw"
Data 90,"Warm Pad",110,"Bagpipe",111,"Fiddle",112,"Shanai"
    end


Home

API Translator

Window Help

Communication

Slider Control

Desktop Shortcuts

Quick Visual Designer

Rules for Code

DATA and Arrays

Easy Polygon

Triangle Draw

Control a Sprite

Shell to DOS

Morse Code

Batch Files

Multicolumn Listbox

Newsletter help

Index